package com.zzyl.serve.service.impl.device;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.zzyl.common.constant.RedisKeyConstant;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.serve.domain.device.Device;
import com.zzyl.serve.dto.device.DeviceDto;
import com.zzyl.serve.mapper.device.DeviceMapper;
import com.zzyl.serve.service.device.IDeviceService;
import com.zzyl.serve.vo.device.DeviceDetailVo;
import com.zzyl.serve.vo.device.IotMsgNotifyData;
import com.zzyl.serve.vo.device.ProductVo;
import org.apache.commons.math3.analysis.function.Add;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.core.Local;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

/**
 * 设备Service业务层处理
 *
 * @author 王崇琪
 * @date 2025-03-14
 */
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService {
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private IoTDAClient client;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 查询设备
     *
     * @param id 设备主键
     * @return 设备
     */
    @Override
    public Device selectDeviceById(Long id) {
        return getById(id);
    }

    /**
     * 查询设备列表
     *
     * @param device 设备
     * @return 设备
     */
    @Override
    public List<Device> selectDeviceList(Device device) {
        return deviceMapper.selectDeviceList(device);
    }

    /**
     * 新增设备
     *
     * @param device 设备
     * @return 结果
     */
    @Override
    public int insertDevice(Device device) {
        return save(device) ? 1 : 0;
    }

    /**
     * 修改设备
     *
     * @param
     * @return 结果
     */
    @Override
    public int updateDevice(DeviceDto dto) {
        // TODO 1.根据前端传来的数据我们只能修改设备名称,接入位置,设备类型

        //创建发送修改请求以及参数
        UpdateDeviceRequest request = new UpdateDeviceRequest();
        request.withDeviceId(dto.getIotId());
        UpdateDevice body = new UpdateDevice();

        //华为云我们只能更改名称
        body.withDeviceName(dto.getDeviceName());
        request.withBody(body);

        UpdateDeviceResponse response = client.updateDevice(request);
        if (response.getHttpStatusCode() != 200) {
            throw new BaseException("修改失败");
        }
        //设备表可以更改设备类型以及接入位置
        Device bean = BeanUtil.toBean(dto, Device.class);
        //在这里查询看是否有相同的老人或者位置绑定了重复的东西
        //然后看当前人或者是设备是否有绑定相同的设备
        long count1 = count(new LambdaQueryWrapper<Device>()
                .eq(Device::getBindingLocation, dto.getBindingLocation())
                .eq(Device::getProductKey, dto.getProductKey())
                .eq(Device::getLocationType, dto.getLocationType())
                .eq(Device::getPhysicalLocationType, dto.getPhysicalLocationType())
        );
        if (count1 > 0) {
            throw new BaseException("重复绑定");
        }

        boolean b = updateById(bean);
        return b ? 1 : 0;


    }

    /**
     * 批量删除设备
     *
     * @param ids 需要删除的设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceByIds(Long[] ids) {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除设备信息
     *
     * @param id 设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceById(Long id) {
        return removeById(id) ? 1 : 0;
    }

    @Override
    public void syncProductList() {
        //首先我们从华为云里面拿到所有的产品列表
        // TODO 1 看华为云接口文档

        ListProductsRequest request = new ListProductsRequest();
        //设置的分页的数据
        request.setLimit(50);
        ListProductsResponse response = client.listProducts(request);
        // 响应的是Products
        if (response.getHttpStatusCode() != 200) {
            throw new BaseException("物联网接口-查询产品,同步失败");
        }
        //自带的类
        List<ProductSummary> products = response.getProducts();
        //然后我们把这个数据放到缓存里面
        redisTemplate.opsForValue().set(RedisKeyConstant.IOT_ALL_PRODUCT_LIST, products);


    }

    /**
     * 查看所有产品列表,从Redis里面拿
     *
     * @return
     */

    @Override
    public List<ProductVo> allProduct() {
        // TODO 1.根据接口文档的要求我们只需要2个属性一个是productId,另一个是name
        //从缓存中拿,因为我们往里存的格式是List<ProductSummary>的,我们要转为
        List<ProductSummary> list = (List<ProductSummary>) redisTemplate.opsForValue().get(RedisKeyConstant.IOT_ALL_PRODUCT_LIST);
        //我们判断是否为空,如果为空,我们就返回一个空的集合
        if (ObjectUtils.isEmpty(list)) {
            return Collections.emptyList();
        }

        //我们要返回的是一个productVo,里面只有2个属性,且都来自List<ProductSummary>,我们使用集合拷贝
        List<ProductVo> productVos = BeanUtil.copyToList(list, ProductVo.class);
        return productVos;
    }

    /**
     * 注册设备
     *
     * @param deviceDto
     */
    @Override
    public void registerDevice(DeviceDto deviceDto) {
        // 1. 判断设备名称是否重复
        long count1 = count(
                new LambdaQueryWrapper<Device>()
                        .eq(StringUtils.isNotBlank(deviceDto.getDeviceName()), Device::getDeviceName, deviceDto.getDeviceName())
        );

        if (count1 > 0) {
            // 设备名称已存在，终止运行
            throw new BaseException("设备名称重复，请重新输入");
        }

        // 2. 判断设备标识码是否重复
        long count2 = count(
                new LambdaQueryWrapper<Device>()
                        .eq(StringUtils.isNotBlank(deviceDto.getNodeId()), Device::getNodeId, deviceDto.getNodeId())
        );
        if (count2 > 0) {
            // 设备名称已存在，终止运行
            throw new BaseException("设备标识码重复，请重新输入");
        }


        // 3. 判断同一位置是否已绑定相同产品
        long count3 = count(
                new LambdaQueryWrapper<Device>()
                        .eq(Device::getBindingLocation, deviceDto.getBindingLocation())
                        .eq(Device::getLocationType, deviceDto.getLocationType())
                        .eq(Device::getPhysicalLocationType, deviceDto.getPhysicalLocationType())
                        .eq(Device::getProductKey, deviceDto.getProductKey())
        );
        if (count3 > 0) {
            // 设备名称已存在，终止运行
            throw new BaseException("同一位置已绑定相同产品，请重新输入");
        }


        // 4. 调用IOT接口，注册设备
        AddDeviceRequest request = new AddDeviceRequest();
        AddDevice body = new AddDevice();

        // 提前生成设备秘钥：
        String secret = UUID.randomUUID().toString().replace("-", "");

        // 封装调用的请求参数
        AuthInfo authInfobody = new AuthInfo();
        authInfobody.withAuthType("SECRET")
                .withSecret(secret);

        body.withDescription(deviceDto.getDeviceDescription());
        body.withAuthInfo(authInfobody);
        body.withProductId(deviceDto.getProductKey());
        body.withDeviceName(deviceDto.getDeviceName());
        body.withNodeId(deviceDto.getNodeId());

        request.withBody(body);

        // 发送请求
        AddDeviceResponse response = new AddDeviceResponse();
        try {
            response = client.addDevice(request);
        }catch (Exception e){
            // 调用失败
            throw new BaseException("调用异常，设备注册失败，请重新输入");

        }

        // 5. 本地保存到设备表
        String deviceId = response.getDeviceId();

        // 属性拷贝
        Device device = BeanUtil.toBean(deviceDto, Device.class);

        device.setIotId(deviceId);
        device.setSecret(secret);

        // 保存：
        save(device);

    }

    /**
     * 查询设备详情
     *
     * @param iotId
     * @return
     */

    @Override
    public DeviceDetailVo queryDeviceDetail(String iotId) {
        // TODO 1.查看接口文档我们只需要从化为云里面拿设备状态和激活时间,搞一个vo

        Device device = getOne(new LambdaQueryWrapper<Device>()
                .eq(Device::getIotId, iotId)
        );
        //拷贝属性
        DeviceDetailVo deviceDetailVo = BeanUtil.toBean(device, DeviceDetailVo.class);

        //创建请求对象,看文档
        ShowDeviceRequest request = new ShowDeviceRequest();
        request.withDeviceId(iotId);
        ShowDeviceResponse response = client.showDevice(request);
        //得到状态
        String status = response.getStatus();
        deviceDetailVo.setDeviceStatus(status);
        //时间有点特殊
        //封装日期时间,需要格式转换,它的默认格式yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
        //z是中国的时区
        //如果为空就说明是未激活,我们直接返回vo
        if (response.getActiveTime() != null) {
            LocalDateTime activeTime = LocalDateTimeUtil.parse(response.getActiveTime(), DatePattern.UTC_MS_PATTERN);
            //日期时区转换
            activeTime = activeTime.atZone(ZoneId.from(ZoneOffset.UTC))
                    .withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                    .toLocalDateTime();
            deviceDetailVo.setActiveTime(activeTime);

        }
        return deviceDetailVo;


    }

    //查看上报数据
    @Override
    public AjaxResult queryServiceProperties(String iotId) {
        // TODO 根据接口文档的示例,返回的结果是list包着几个对象,由于这个返回值只有这个地方用,所以我们使用map集合
        //发送请求
        ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();
        request.withDeviceId(iotId);
        //得到响应
        ShowDeviceShadowResponse response = client.showDeviceShadow(request);
        if (response.getHttpStatusCode() != 200) {
            throw new BaseException("查询失败");
        }
        //从响应中拿到我们所想要的数据,这个shadow是一个list集合里面有三个对象,我们遍历得出我们想要的
        List<DeviceShadowData> shadow = response.getShadow();
        //如果为空
        if (CollUtil.isEmpty(shadow)) {
            List<Object> emptyList = Collections.emptyList();
            return AjaxResult.success(emptyList);
        }
        //然后我们根据华为云响应的示例我们得出来数据,我们不知道具体有几个属性,所以我们直接求一个json对象children
        JSONObject jsonObject = JSONUtil.parseObj(shadow.get(0).getReported().getProperties());
        //我们创一个list集合去存它,我们的预想的是list里面装着map
        List<Map<String, Object>> list = new ArrayList<>();
        //得到响响应的时间
        LocalDateTime activeTime = LocalDateTimeUtil.parse(
                shadow.get(0).getReported().getEventTime(),
                "yyyyMMdd'T'HHmmss'Z'"
        );
        //日期时区转换
        LocalDateTime eventTime = activeTime.atZone(ZoneId.from(ZoneOffset.UTC))
                .withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                .toLocalDateTime();
        //存储数据
        jsonObject.forEach((key, value) -> {
            Map<String, Object> map = new HashMap<>();
            map.put("functionId", key);
            map.put("value", value);
            map.put("eventTime", eventTime);
            list.add(map);

        });
        return AjaxResult.success(list);


    }

    @Override
    public void batchInsertDeviceData(IotMsgNotifyData iotMsgNotifyData) {

    }

    /**
     * 删除设备
     *
     * @param id
     */
    @Override
    public void deleteByIotId(String id) {
        DeleteDeviceRequest request = new DeleteDeviceRequest();
        request.withDeviceId(id);

        DeleteDeviceResponse response = client.deleteDevice(request);

        //删除成功之后再从表里面删除
        Device one = getOne(new LambdaQueryWrapper<Device>().eq(Device::getIotId, id));
        removeById(one);
    }


}
